package com.tqw.es.service.impl;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.tqw.es.pojo.Item;
import com.tqw.es.repository.ItemRepository;
import com.tqw.es.service.SearchService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * @author Jim
 */
@Slf4j
@Service
public class SearchServiceImpl implements SearchService {
    @Autowired
    private ItemRepository itemRepository;
    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * 根据关键词搜索商品优先从缓存获取
     * @param key 关键词
     * @param pageable 分页参数
     * @return 商品数据
     */
    @Override
    public List<Item> search(String key, Pageable pageable){
        if (StringUtils.isEmpty(key)) {
            return null;
        }
        String trimKey = key.trim();
        if (StringUtils.isEmpty(trimKey)) {
            return null;
        }
        ValueOperations<String,String> vo = null;
        String searchCache = null;
        if (pageable.getPageNumber()>5) {
            vo = redisTemplate.opsForValue();
            searchCache = vo.get(trimKey+pageable.getPageNumber()+pageable.getPageSize());
        }
        ObjectMapper objectMapper = new ObjectMapper();
        if(searchCache!=null){
            try {
                return objectMapper.readValue(searchCache, new TypeReference<List<Item>>(){});
            } catch (JsonProcessingException e) {
                log.error("com.tqw.es.service.impl.SearchServiceImpl.search {} caused by redis deserialization exception", e.getMessage());
            }
        }
        List<SearchHit<Item>> hits = itemRepository.findByTitle(trimKey, pageable);
        if(hits.size()==0){
            //缓存null值减少缓存穿透
            vo.set(trimKey+pageable.getPageNumber()+pageable.getPageSize(),null, Duration.ofSeconds(10));
            return null;
        }
        List<Item> items = toItemList(hits);
        if (pageable.getPageNumber()>5) {
            String json = null;
            try {
                json = objectMapper.writeValueAsString(items);
            } catch (JsonProcessingException e) {
                log.error("com.tqw.es.service.impl.SearchServiceImpl.search {} caused by redis serialization exception",e.getMessage());
            }
            int n = new Random().nextInt(61)+30;
            //缓存时间设置随机减少缓存雪崩
            vo.set(trimKey+pageable.getPageNumber()+pageable.getPageSize(), json, Duration.ofSeconds(n));
        }
        return items;
    }

    /**
     * 把商品数据转换成高亮商品数据
     * @param list 需要转换的商品集合
     * @return 高亮商品数据集合
     */
    private List<Item> toItemList(List<SearchHit<Item>> list) {
        List<Item> items = new ArrayList<>();
        for (SearchHit<Item> sh : list) {
            String title = concat(sh.getHighlightField("title"));
            Item item = sh.getContent();
            item.setTitle(title);
            items.add(item);
        }
        return items;
    }

    /**
     * 根据集合拼接字符串
     * @param list 需要拼接的字符串集合
     * @return 拼接后的字符串
     */
    private String concat(List<String> list) {
        StringBuilder sb = new StringBuilder();
        for (String s : list) {
            sb.append(s);
        }
        return sb.toString();
    }
}
